#ifndef THREAD_UTILS_H
#define THREAD_UTILS_H

#include <string.h>

#include <list>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <thread>
#include <pthread.h>

#include "core.h"

template <typename T>
class BlockingQueue
{
public:
    typedef typename std::list<T>::size_type sz_t;

    static const sz_t NO_LIMIT = -1;

    BlockingQueue(sz_t maxSize = NO_LIMIT)
        : m_max_size(maxSize) {}

    void add(const T &elem);

    T take();

    T peek() const;

    sz_t size() const;

private:

    bool _has_capacity()
    {
        return m_max_size <= 0 || m_list.size() < m_max_size;
    }

    const sz_t m_max_size;

    mutable std::mutex m_mutex;
    mutable std::condition_variable m_cond_has_elem;
    mutable std::condition_variable m_cond_has_capa;
    std::list<T> m_list;

};

template <typename T>
void BlockingQueue<T>::add(const T &elem)
{
    std::unique_lock<std::mutex> lock(m_mutex);

    while (!_has_capacity()) {
        m_cond_has_capa.wait(lock);
    }

    m_list.push_back(elem);
    m_cond_has_elem.notify_one();
}

template <typename T>
T BlockingQueue<T>::take()
{
    std::unique_lock<std::mutex> lock(m_mutex);

    while (m_list.empty()) {
        m_cond_has_elem.wait(lock);
    }

    // 显式拷贝构造一个新的对象，稍后把这个新的对象返回
    T elem(m_list.front());
    m_list.pop_front();

    m_cond_has_capa.notify_one();
    return elem;
}

template <typename T>
T BlockingQueue<T>::peek() const
{
    std::unique_lock<std::mutex> lock(m_mutex);

    while (m_list.empty()) {
        m_cond_has_elem.wait(lock);
    }

    return m_list.front();
}

template <typename T>
typename BlockingQueue<T>::sz_t BlockingQueue<T>::size() const
{
    std::unique_lock<std::mutex> lock(m_mutex);

    return m_list.size();
}


class Executor
{
public:
    virtual void exe(std::function<void(void)> action) = 0;
    virtual void stop() = 0;
    virtual void join() = 0;
    virtual ~Executor() {}
};

class SingleThreadExecutor : public Executor
{
public:

    SingleThreadExecutor();

    void exe(std::function<void(void)> action);

    void stop();

    void join();

private:

    void _body();

    std::atomic_bool m_running;
    std::thread m_thread;
    BlockingQueue<std::function<void(void)>> m_queue;
};

class RWMutex
{
public:
    RWMutex()
    {
        int n = pthread_rwlock_init(&m_mutex, nullptr);
        if (n != 0) {
            errno = n;
            ERROR(strerror(n));
        }
    }

    ~RWMutex()
    {
        int n = pthread_rwlock_destroy(&m_mutex);
        if (n != 0) {
            errno = n;
            ERROR(strerror(n));
        }
    }

    void rlock()
    {
        int n = pthread_rwlock_rdlock(&m_mutex);
        if (n != 0) {
            errno = n;
            ERROR(strerror(n));
        }
    }

    void wlock()
    {
        int n = pthread_rwlock_wrlock(&m_mutex);
        if (n != 0) {
            errno = n;
            ERROR(strerror(n));
        }
    }

    void unlock()
    {
        int n = pthread_rwlock_unlock(&m_mutex);
        if (n != 0) {
            errno = n;
            ERROR(strerror(n));
        }
    }

private:
    pthread_rwlock_t m_mutex;
};

class ReadLocker
{
public:
    ReadLocker(RWMutex &mutex):m_mutex(mutex) {mutex.rlock();}
    ~ReadLocker() {m_mutex.unlock();}
private:
    RWMutex &m_mutex;
};

class WriteLocker
{
public:
    WriteLocker(RWMutex &mutex):m_mutex(mutex) {mutex.wlock();}
    ~WriteLocker() {m_mutex.unlock();}
private:
    RWMutex &m_mutex;
};

#endif // THREAD_UTILS_H

